//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.TridentComposer
{
    using System;
    using SR = Microsoft.Research.DataLayer;

    /// <summary>
    /// External registry proxy. this has the functionality of importing a new data provider in a seperate appdomain.
    /// </summary>
    public class ExternalRegistryProxy : MarshalByRefObject
    {
        /// <summary>
        /// Regitry connection.
        /// </summary>
        private SR.Connection registryConnection;

        /// <summary>
        /// Assembly path of the data reference provider.
        /// </summary>
        private string assemplyPath;

        /// <summary>
        /// Initializes a new instance of the <see cref="ExternalRegistryProxy"/> class.
        /// </summary>
        private ExternalRegistryProxy()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ExternalRegistryProxy"/> class.
        /// </summary>
        /// <param name="connection">The connection.</param>
        /// <param name="assemblypath">The assemblypath.</param>
        public ExternalRegistryProxy(SR.Connection connection, string assemblyPath)
        {
            this.registryConnection = connection;
            this.assemplyPath = assemblyPath;
        }

        /// <summary>
        /// Executes this instance.
        /// </summary>
        /// <returns></returns>
        public bool? Execute()
        {
            AppDomain childAppdomain = null;
            try
            {
                childAppdomain = ExternalRegistryProxy.CreateAppDomain();

                ExternalRegistryProxy importProxy = childAppdomain.CreateInstanceAndUnwrap
                    (typeof(ExternalRegistryProxy).Assembly.FullName,
                        typeof(ExternalRegistryProxy).FullName,
                        false,
                        System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance,
                        null,
                        new object[] { this.registryConnection, this.assemplyPath },
                        System.Globalization.CultureInfo.InvariantCulture,
                        null) as ExternalRegistryProxy;

                importProxy.Init(this.registryConnection, this.assemplyPath);
                return importProxy.ImportDataReferenceProvider();
            }
            finally
            {
                ExternalRegistryProxy.UnloadAppDomain(childAppdomain);
            }
        }

        #region private methods
        /// <summary>
        /// Initailize the new object created in another appdomain.
        /// </summary>
        /// <param name="registryConnection">The registry connection.</param>
        /// <param name="fileReferences">The file references.</param>
        /// <param name="createNewVersionOnNameMatch">True if we need to create new version if the name matches. False otherwise.</param>
        private void Init(SR.Connection registryConnection, string assemblypath)
        {
            this.registryConnection = registryConnection;
            this.assemplyPath = assemblypath;

            SR.SR_Connection.Init();
            this.registryConnection.Open();
            this.registryConnection.AutoSave = false;
        }

        /// <summary>
        /// Import the data reference provider.
        /// </summary>
        /// <returns>True if Import succeeded; False otherwise.</returns>
        private bool? ImportDataReferenceProvider()
        {
            bool? importStatus = false;

            ExternalRegistry import = new ExternalRegistry(this.registryConnection, false);
            importStatus = import.ImportDataReferenceProvider(this.assemplyPath);

            return importStatus;
        }

        /// <summary>
        /// Creates a new app domain.
        /// </summary>
        /// <returns>New instance of the app domain.</returns>
        private static AppDomain CreateAppDomain()
        {
            AppDomain newDomain = AppDomain.CreateDomain("ImportProviderAppdomain");
            return newDomain;
        }

        /// <summary>
        /// Unloads the executor app domain.
        /// </summary>
        /// <param name="executorDomain">Instance of the AppDomain.</param>
        private static void UnloadAppDomain(AppDomain appDomain)
        {
            if (appDomain != null)
            {
                AppDomain.Unload(appDomain);
            }
        }
        #endregion
    }
}
